package com.apigen.platform.service.impl;

import com.apigen.platform.dto.CodeGenerationRequest;
import com.apigen.platform.entity.GenTable;
import com.apigen.platform.entity.GenTableField;
import com.apigen.platform.service.TemplateRenderService;
import com.apigen.platform.util.DatabaseTypeMapper;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 模板渲染服务实现类
 *
 * @author API Generator Platform
 * @since 2025-10-03
 */
@Slf4j
@Service
public class TemplateRenderServiceImpl implements TemplateRenderService {

    private final Handlebars handlebars;

    public TemplateRenderServiceImpl() {
        this.handlebars = new Handlebars();
        registerHelpers();
    }

    @Override
    public String renderTemplate(String templateContent, Map<String, Object> data) {
        try {
            Template template = handlebars.compileInline(templateContent);
            return template.apply(data);
        } catch (IOException e) {
            log.error("模板渲染失败: {}", e.getMessage());
            throw new RuntimeException("模板渲染失败: " + e.getMessage(), e);
        }
    }

    @Override
    public Map<String, Object> generateTableData(GenTable table, List<GenTableField> fields, CodeGenerationRequest config) {
        Map<String, Object> data = new HashMap<>();

        // 基础信息
        data.put("basePackage", config.getPackageName());
        data.put("entityName", toPascalCase(table.getTableName()));
        data.put("tableName", table.getTableName());
        data.put("tableComment", StringUtils.hasText(table.getTableComment()) ? table.getTableComment() : table.getTableName());
        data.put("author", config.getAuthor());
        data.put("version", config.getVersion());
        data.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

        // 处理字段信息
        List<Map<String, Object>> fieldList = fields.stream().map(field -> {
            Map<String, Object> fieldData = new HashMap<>();
            fieldData.put("fieldName", toCamelCase(field.getFieldName()));
            fieldData.put("columnName", field.getFieldName());
            fieldData.put("javaType", DatabaseTypeMapper.mapToJavaType(field.getFieldType(), field.getFieldLength(), field.getFieldPrecision()));
            fieldData.put("jdbcType", DatabaseTypeMapper.mapToJdbcType(field.getFieldType()));
            fieldData.put("comment", StringUtils.hasText(field.getFieldComment()) ? field.getFieldComment() : field.getFieldName());
            fieldData.put("isPrimaryKey", field.getIsPrimaryKey());
            fieldData.put("required", !field.getIsNullable());
            fieldData.put("isLogicDelete", "deleted".equals(field.getFieldName()));
            fieldData.put("queryType", determineQueryType(field));
            fieldData.put("maxLength", field.getFieldLength());
            fieldData.put("example", generateFieldExample(field));
            return fieldData;
        }).collect(Collectors.toList());

        data.put("fields", fieldList);

        // 主键字段
        Optional<Map<String, Object>> primaryKey = fieldList.stream()
                .filter(f -> Boolean.TRUE.equals(f.get("isPrimaryKey")))
                .findFirst();
        data.put("primaryKey", primaryKey.orElse(null));

        // 查询字段
        List<Map<String, Object>> queryFields = fieldList.stream()
                .filter(f -> !"none".equals(f.get("queryType")))
                .collect(Collectors.toList());
        data.put("queryFields", queryFields);

        // 必填字段
        List<Map<String, Object>> requiredFields = fieldList.stream()
                .filter(f -> Boolean.TRUE.equals(f.get("required")) && !Boolean.TRUE.equals(f.get("isPrimaryKey")))
                .collect(Collectors.toList());
        data.put("requiredFields", requiredFields);

        // 导入类型
        Set<String> importTypes = fieldList.stream()
                .map(f -> DatabaseTypeMapper.getImportStatement((String) f.get("javaType")))
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        data.put("importTypes", new ArrayList<>(importTypes));

        // 关联信息（暂时不支持）
        data.put("hasRelations", false);
        data.put("relations", new ArrayList<>());

        // 自定义查询（暂时不支持）
        data.put("hasCustomQueries", false);
        data.put("customQueries", new ArrayList<>());

        return data;
    }

    @Override
    public Map<String, Object> generateProjectData(CodeGenerationRequest config) {
        Map<String, Object> data = new HashMap<>();

        data.put("groupId", config.getPackageName());
        data.put("artifactId", config.getProjectName().toLowerCase().replaceAll("\\s+", "-"));
        data.put("version", config.getVersion());
        data.put("projectName", config.getProjectName());
        data.put("description", config.getDescription());
        data.put("author", config.getAuthor());
        data.put("basePackage", config.getPackageName());
        data.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        data.put("currentYear", LocalDateTime.now().getYear());

        // 框架配置
        data.put("springBootVersion", "2.7.18");
        data.put("javaVersion", "8");
        data.put("knife4jVersion", "4.4.0");
        data.put("mybatisPlusVersion", "3.5.3");
        data.put("druidVersion", "1.2.18");
        data.put("pageHelperVersion", "1.4.6");
        data.put("mysqlVersion", "8.0.33");
        data.put("jwtVersion", "0.11.5");
        data.put("commonsIoVersion", "2.11.0");

        // 功能开关
        data.put("enableSecurity", config.getConfig() != null && config.getConfig().getEnableValidation());
        data.put("enableRedis", config.getConfig() != null && config.getConfig().getEnableCache());
        data.put("enableActuator", true);
        data.put("enableGenerator", true);
        data.put("enableDruid", true);
        data.put("enablePageHelper", true);
        data.put("enableJwt", config.getConfig() != null && config.getConfig().getEnableValidation());
        data.put("enableAliyunRepo", true);
        data.put("enableFileUpload", false);
        data.put("enableSqlLog", true);

        // 数据库配置
        if (config.getConfig() != null && config.getConfig().getDatabase() != null) {
            Map<String, Object> dbConfig = new HashMap<>();
            dbConfig.put("driverClassName", getDriverClassName(config.getDatabaseType()));
            dbConfig.put("url", buildDatabaseUrl(config.getConfig().getDatabase()));
            dbConfig.put("username", config.getConfig().getDatabase().getUsername());
            dbConfig.put("password", config.getConfig().getDatabase().getPassword());
            data.put("databaseConfig", dbConfig);
        }

        // Redis配置
        if (config.getConfig() != null && config.getConfig().getRedis() != null) {
            data.put("redisConfig", config.getConfig().getRedis());
        }

        // JWT配置
        if (config.getConfig() != null && config.getConfig().getJwt() != null) {
            data.put("jwtConfig", config.getConfig().getJwt());
        }

        // Druid配置
        Map<String, Object> druidConfig = new HashMap<>();
        druidConfig.put("initialSize", 5);
        druidConfig.put("minIdle", 10);
        druidConfig.put("maxActive", 20);
        druidConfig.put("maxWait", 60000);
        druidConfig.put("timeBetweenEvictionRunsMillis", 60000);
        druidConfig.put("minEvictableIdleTimeMillis", 300000);
        druidConfig.put("slowSqlMillis", 5000);
        druidConfig.put("allow", "127.0.0.1,192.168.1.1");
        druidConfig.put("deny", "192.168.1.73");
        druidConfig.put("loginUsername", "admin");
        druidConfig.put("loginPassword", "123456");
        data.put("druidConfig", druidConfig);

        // MyBatis配置
        Map<String, Object> mybatisConfig = new HashMap<>();
        mybatisConfig.put("idType", "ASSIGN_ID");
        mybatisConfig.put("fieldStrategy", "NOT_EMPTY");
        mybatisConfig.put("enableLogicDelete", true);
        mybatisConfig.put("logicDeleteField", "deleted");
        mybatisConfig.put("logicDeleteValue", 1);
        mybatisConfig.put("logicNotDeleteValue", 0);
        mybatisConfig.put("tablePrefix", "");
        data.put("mybatisConfig", mybatisConfig);

        // 日志配置
        Map<String, Object> logConfig = new HashMap<>();
        logConfig.put("maxSize", "10MB");
        logConfig.put("maxHistory", 30);
        data.put("logConfig", logConfig);

        // 应用配置
        data.put("port", 8080);
        data.put("contextPath", "/");
        data.put("applicationName", config.getProjectName());
        data.put("logLevel", "DEBUG");
        data.put("sqlLogLevel", "DEBUG");
        data.put("securityLogLevel", "DEBUG");
        data.put("authorEmail", "support@example.com");
        data.put("authorUrl", "https://example.com");
        data.put("domain", "api.example.com");

        return data;
    }

    @Override
    public String getTemplateContent(String frameworkType, String templateName) {
        String templatePath = String.format("templates/%s/%s", frameworkType, templateName);
        
        try {
            ClassPathResource resource = new ClassPathResource(templatePath);
            if (resource.exists()) {
                try (InputStream inputStream = resource.getInputStream()) {
                    byte[] bytes = new byte[inputStream.available()];
                    inputStream.read(bytes);
                    return new String(bytes, StandardCharsets.UTF_8);
                }
            } else {
                log.warn("模板文件不存在: {}", templatePath);
                return "";
            }
        } catch (IOException e) {
            log.error("读取模板文件失败: {}", templatePath, e);
            throw new RuntimeException("读取模板文件失败: " + templatePath, e);
        }
    }

    @Override
    public List<Map<String, Object>> getFrameworkTemplates(String frameworkType) {
        List<Map<String, Object>> templates = new ArrayList<>();
        
        // Spring Boot框架的模板列表
        if ("spring-boot".equals(frameworkType)) {
            templates.add(createTemplateInfo("controller.hbs", "Controller", "controller", "REST控制器模板"));
            templates.add(createTemplateInfo("service.hbs", "Service Interface", "service", "业务逻辑服务接口模板"));
            templates.add(createTemplateInfo("service-impl.hbs", "Service Implementation", "service", "业务逻辑服务实现模板"));
            templates.add(createTemplateInfo("entity.hbs", "Entity", "model", "MyBatis Plus实体模板"));
            templates.add(createTemplateInfo("dto.hbs", "DTO", "model", "数据传输对象模板"));
            templates.add(createTemplateInfo("vo.hbs", "VO", "model", "视图对象模板"));
            templates.add(createTemplateInfo("mapper.hbs", "Mapper Interface", "service", "MyBatis Mapper接口模板"));
            templates.add(createTemplateInfo("mapper.xml.hbs", "Mapper XML", "config", "MyBatis Mapper XML模板"));
            templates.add(createTemplateInfo("result.hbs", "Result", "model", "统一返回结果模板"));
            templates.add(createTemplateInfo("page-result.hbs", "PageResult", "model", "分页返回结果模板"));
            templates.add(createTemplateInfo("error-code.hbs", "ErrorCode", "model", "错误码枚举模板"));
            templates.add(createTemplateInfo("knife4j-config.hbs", "Knife4j Config", "config", "Knife4j配置类"));
            templates.add(createTemplateInfo("pom.xml.hbs", "POM XML", "config", "Maven项目配置文件"));
            templates.add(createTemplateInfo("application.yml.hbs", "Application YAML", "config", "Spring Boot配置文件"));
        }
        
        return templates;
    }

    @Override
    public String toCamelCase(String str) {
        if (!StringUtils.hasText(str)) {
            return str;
        }
        
        String[] parts = str.split("_");
        StringBuilder result = new StringBuilder(parts[0].toLowerCase());
        
        for (int i = 1; i < parts.length; i++) {
            result.append(StringUtils.capitalize(parts[i].toLowerCase()));
        }
        
        return result.toString();
    }

    @Override
    public String toPascalCase(String str) {
        if (!StringUtils.hasText(str)) {
            return str;
        }
        
        String camelCase = toCamelCase(str);
        return StringUtils.capitalize(camelCase);
    }

    @Override
    public String toUnderscoreCase(String str) {
        if (!StringUtils.hasText(str)) {
            return str;
        }
        
        return str.replaceAll("([A-Z])", "_$1").toLowerCase().replaceAll("^_", "");
    }

    /**
     * 注册Handlebars辅助函数
     */
    private void registerHelpers() {
        // 转换为小驼峰命名
        handlebars.registerHelper("lowerCase", (Helper<String>) (context, options) -> {
            if (context == null) return "";
            return toCamelCase(context);
        });

        // 转换为大驼峰命名
        handlebars.registerHelper("upperFirst", (Helper<String>) (context, options) -> {
            if (context == null) return "";
            return toPascalCase(context);
        });

        // 转换为下划线命名
        handlebars.registerHelper("underScore", (Helper<String>) (context, options) -> {
            if (context == null) return "";
            return toUnderscoreCase(context);
        });

        // 条件判断
        handlebars.registerHelper("eq", (Helper<Object>) (context, options) -> {
            Object param = options.param(0);
            return Objects.equals(context, param) ? options.fn() : options.inverse();
        });

        handlebars.registerHelper("ne", (Helper<Object>) (context, options) -> {
            Object param = options.param(0);
            return !Objects.equals(context, param) ? options.fn() : options.inverse();
        });

        // 逻辑运算
        handlebars.registerHelper("and", (Helper<Boolean>) (context, options) -> {
            Boolean param = options.param(0);
            return (context && param) ? options.fn() : options.inverse();
        });

        handlebars.registerHelper("or", (Helper<Boolean>) (context, options) -> {
            Boolean param = options.param(0);
            return (context || param) ? options.fn() : options.inverse();
        });

        handlebars.registerHelper("not", (Helper<Boolean>) (context, options) -> {
            return !context ? options.fn() : options.inverse();
        });
    }

    /**
     * 确定字段的查询类型
     */
    private String determineQueryType(GenTableField field) {
        String javaType = DatabaseTypeMapper.mapToJavaType(field.getFieldType(), field.getFieldLength(), field.getFieldPrecision());
        
        if (DatabaseTypeMapper.isStringType(javaType)) {
            return "like";
        } else if (DatabaseTypeMapper.isDateTimeType(javaType)) {
            return "ge"; // 大于等于，用于日期范围查询
        } else {
            return "eq";
        }
    }

    /**
     * 生成字段示例值
     */
    private String generateFieldExample(GenTableField field) {
        String javaType = DatabaseTypeMapper.mapToJavaType(field.getFieldType(), field.getFieldLength(), field.getFieldPrecision());
        
        switch (javaType) {
            case "String":
                return "示例文本";
            case "Integer":
            case "Long":
                return "1";
            case "Float":
            case "Double":
                return "1.0";
            case "BigDecimal":
                return "1.00";
            case "Boolean":
                return "true";
            case "LocalDate":
                return "2023-01-01";
            case "LocalTime":
                return "12:00:00";
            case "LocalDateTime":
                return "2023-01-01 12:00:00";
            default:
                return "";
        }
    }

    /**
     * 创建模板信息
     */
    private Map<String, Object> createTemplateInfo(String path, String name, String type, String description) {
        Map<String, Object> template = new HashMap<>();
        template.put("name", name);
        template.put("path", "/templates/spring-boot/" + path);
        template.put("type", type);
        template.put("description", description);
        return template;
    }

    /**
     * 获取数据库驱动类名
     */
    private String getDriverClassName(String databaseType) {
        switch (databaseType.toUpperCase()) {
            case "MYSQL":
                return "com.mysql.cj.jdbc.Driver";
            case "POSTGRESQL":
                return "org.postgresql.Driver";
            case "ORACLE":
                return "oracle.jdbc.OracleDriver";
            case "SQL_SERVER":
                return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
            default:
                return "com.mysql.cj.jdbc.Driver";
        }
    }

    /**
     * 构建数据库连接URL
     */
    private String buildDatabaseUrl(CodeGenerationRequest.DatabaseConfig config) {
        switch ("MYSQL") { // 暂时固定为MYSQL
            case "MYSQL":
                return String.format("jdbc:mysql://%s:%d/%s?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%%2B8",
                        config.getHost(), config.getPort(), config.getDatabase());
            case "POSTGRESQL":
                return String.format("jdbc:postgresql://%s:%d/%s", config.getHost(), config.getPort(), config.getDatabase());
            case "ORACLE":
                return String.format("jdbc:oracle:thin:@%s:%d:%s", config.getHost(), config.getPort(), config.getDatabase());
            case "SQL_SERVER":
                return String.format("jdbc:sqlserver://%s:%d;databaseName=%s", config.getHost(), config.getPort(), config.getDatabase());
            default:
                return String.format("jdbc:mysql://%s:%d/%s?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%%2B8",
                        config.getHost(), config.getPort(), config.getDatabase());
        }
    }
}
